package com.sd.repay.service.impl;

import com.sd.repay.common.CommonResult;
import com.sd.repay.common.Constants;
import com.sd.repay.pojo.PlanConfig;
import com.sd.repay.pojo.RepayPlan;
import com.sd.repay.pojo.RepayPlanDetail;
import com.sd.repay.pojo.TransferOrder;
import com.sd.repay.service.*;
import com.sd.repay.utils.DateUtil;
import com.sd.repay.utils.JsonUtil;
import com.sd.repay.utils.WebUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author sd
 * @Description: 还款任务Service实现
 * @date 2018年08月25日
 */
@Service
public class RepayTaskServiceImpl implements RepayTaskService {

    private static Logger log = LoggerFactory.getLogger(RepayTaskServiceImpl.class);

    @Autowired
    private RepayPlanService planService;
    @Autowired
    private RepayPlanDetailService planDetailService;
    @Autowired
    private CardManageService cardManageService;
    @Autowired
    private WithdrawOrderService withdrawOrderService;
    @Autowired
    private RepayService repayService;


    /**
     * 执行还款计划
     *
     * @param plan
     * @throws Exception
     */
    @Override
    @Transactional
    public void executeRepayTask(RepayPlan plan) throws Exception {
        log.info("准备执行还款计划：" + plan.getBatchNo());

        Date currDate = new Date();
        int currHour = Integer.parseInt(DateUtil.format(currDate, "HH"));

        String batchNo = plan.getBatchNo();
        String merchantNo = plan.getMerchantNo();
        String cardNo = plan.getCardNo();
        String repayType = plan.getRepayType();
        Date planBeginDate = plan.getRepayBeginTime();
        Date planEndDate = plan.getRepayEndTime();
        String planAcqCode = plan.getAcqCode();

        //根据通道编码获取支付接口
        QuickPayService quickPayService = WebUtil.getQuickPayServiceByAcqCode(planAcqCode);
        if (quickPayService == null) {
            log.info("不支持批次号" + batchNo + "对应的通道编码：" + planAcqCode);
            return;
        }
        //校验通道信息
        Map<String, Object> planChannel = WebUtil.getPayChannelInfo(planAcqCode);
        log.info("批次号" + batchNo + "对应的的通道信息：" + JsonUtil.objectToJson(planChannel));
        if (planChannel == null || planChannel.isEmpty()) {

            List<RepayPlanDetail> queryDetails = planDetailService.getDetailByBatchNoAndStatus(batchNo, Constants.PLAN_DETAIL_STATUS_IN_EXECUTE);
            if (CollectionUtils.isEmpty(queryDetails)) {

                RepayPlanDetail nextDetail = planDetailService.getNextExeuteDetailByBatchNo(batchNo);
                if (Constants.PLAN_DETAIL_TYPE_OUT.equalsIgnoreCase(nextDetail.getPlanType()) && !Constants.PLAN_DETAIL_STATUS_IN_EXECUTE.equals(nextDetail.getPlanStatus())) {

                    log.info("批次号" + batchNo + "对应" + plan.getAcqCode() + "的通道不存在且没有执行中的明细记录，故将计划置为失败");
                    int count = planService.updateRepayPlanStatus(batchNo, Constants.PLAN_STATUS_FAIL, "计划逾期，系统自动置为失败", "计划逾期，还款失败");
                    log.info("批次号" + batchNo + "对应" + plan.getAcqCode() + "的通道不存在且没有执行中的明细记录，计划置为失败影响的行数为：" + count);
                    return;
                }
            }
            return;
        }
        //通道允许还款时间
        int acqAllowBeginTime = Integer.parseInt(String.valueOf(planChannel.get("allow_begin_time")));
        int acqAllowEndTime = Integer.parseInt(String.valueOf(planChannel.get("allow_end_time")));
        if (currHour < acqAllowBeginTime || currHour >= acqAllowEndTime + 1) {
            log.info("批次号" + batchNo + "当前时间不在通道允许还款时间段内");
            return;
        }

        //获取计划配置允许还款时间
        PlanConfig planConfig = WebUtil.getPlanConfigByType(repayType);
        if (null == planConfig) {
            log.info("批次号" + batchNo + "对应的计划类型配置不存在");
            return;
        }
        int repayAllowBeginTime = planConfig.getRepayAllowBeginHour();
        int repayAllowEndTime = planConfig.getRepayAllowEndHour();
        if (currHour < repayAllowBeginTime || currHour >= repayAllowEndTime + 1) {
            log.info("批次号" + batchNo + "当前时间不在计划类型允许还款时间段内");
            return;
        }

        //如果当前时间在还款结束时间之后
        if (DateUtil.daysBetween(currDate, planEndDate) < 0) {
            //获取还款中的明细
            List<RepayPlanDetail> queryDetails = planDetailService.getDetailByBatchNoAndStatus(batchNo, Constants.PLAN_DETAIL_STATUS_IN_EXECUTE);

            if (CollectionUtils.isEmpty(queryDetails)) {

                //没有还款中的明细，取下一笔需要执行的明细，如果是消费，就表示不欠用户钱，可以置为失败状态
                RepayPlanDetail nextDetail = planDetailService.getNextExeuteDetailByBatchNo(batchNo);
                if (Constants.PLAN_DETAIL_TYPE_OUT.equalsIgnoreCase(nextDetail.getPlanType()) && !Constants.PLAN_DETAIL_STATUS_IN_EXECUTE.equals(nextDetail.getPlanStatus())) {

                    log.info("还款计划批次号：" + plan.getBatchNo() + "已经过了还款时间且没有还款中的明细记录，故将计划置为失败");
                    int count = planService.updateRepayPlanStatus(batchNo, Constants.PLAN_STATUS_FAIL, "计划逾期，系统自动置为失败", "计划逾期，还款失败");
                    log.info("还款计划批次号：" + plan.getBatchNo() + "已经过了还款时间且没有执行中的明细记录，计划置为失败影响的行数为：" + count);
                    return;
                }
            }
        }

        //获取计划下一个需要执行的明细
        RepayPlanDetail nextDetail = planDetailService.getNextExeuteDetailByBatchNo(batchNo);
        log.info("批次号" + batchNo + "下一笔需要执行的计划明细：" + JsonUtil.objectToJson(nextDetail));

        //如果执行时间小于当前时间，需要执行还款或消费
        if (nextDetail != null && nextDetail.getPlanTime().compareTo(currDate) <= 0) {

            String planType = nextDetail.getPlanType();
            String planNo = nextDetail.getPlanNo();
            int planIndex = nextDetail.getPlanIndex();
            String planStatus = nextDetail.getPlanStatus();
            BigDecimal planAmount = nextDetail.getPlanAmount();

            log.info("批次号" + batchNo + "准备执行还款明细" + JsonUtil.objectToJson(nextDetail));

            //每天允许最多还款次数
            int dayMaxRepayNum = planConfig.getDayMaxNum();

            //先获取同一批次上一条执行成功的明细
            RepayPlanDetail prevPlanDetail = planDetailService.getPrevSuccessDetailByBatchNo(batchNo, planIndex);

            //如果需要执行的明细为代付类型
            if (Constants.PLAN_DETAIL_TYPE_IN.equals(planType)) {

                //获取当天此卡片已成功还款的次数
                int inSuccNum = withdrawOrderService.getSuccWithdrawCountByCardAndService(cardNo, Constants.WITHDRAW_SERVICE_REPAY);
                if (inSuccNum >= dayMaxRepayNum) {
                    log.info("计划明细编号:" + planNo + "对应卡片当天已经超过每天最多还款次数" + inSuccNum + ">=" + dayMaxRepayNum);
                    return;
                }
                //1、当前计划为首次计划
                if (planIndex == 1) {

                    //将还款计划改为执行中
                    log.info("执行批次号" + batchNo + "的第一条还款明细，将此计划状态置为为执行中");
                    int count = planService.updateRepayPlanStatus(batchNo, Constants.PLAN_STATUS_IN_EXECUTE, Constants.PLAN_STATUS_UN_EXECUTE, "");
                    log.info("修改还款计划批次号：" + batchNo + "状态为执行中影响的行数：" + count);

                    implRepay(plan, nextDetail);
                }
                //2、同一批次上一计划为成功
                if (null != prevPlanDetail) {
                    log.info("计划明细编号" + planNo + "为还款,且上一条明细已执行成功,准备发起代付接口");
                    implRepay(plan, nextDetail);
                }
            } else if (Constants.PLAN_DETAIL_TYPE_OUT.equals(planType)) {

                //同一批次上一计划为成功
                if (null != prevPlanDetail) {
                    log.info("计划明细编号" + planNo + "为消费,且上一条明细已执行成功,准备发起快捷消费接口");
                    implQuickPay(plan, nextDetail, quickPayService);
                }
            }
        }
    }

    /**
     * 发起代付
     *
     * @param repayPlan  还款计划
     * @param planDetail 还款明细
     */
    public void implRepay(RepayPlan repayPlan, RepayPlanDetail planDetail) {

        Map<String, String> repayParam = new HashMap<>();

        String batchNo = repayPlan.getBatchNo();
        String cardNo = planDetail.getCardNo();
        String amount = planDetail.getPlanAmount().toString();
        String planNo = planDetail.getPlanNo();
        String merchantNo = planDetail.getMerchantNo();
        String acqCode = planDetail.getAcqCode();
        int planIndex = planDetail.getPlanIndex();

        repayParam.put("acqCode", acqCode);
        repayParam.put("cardNo", cardNo);
        repayParam.put("amount", amount);
        repayParam.put("planNo", planNo);
        repayParam.put("merchantNo", merchantNo);

        try {

            //更新还款计划，防止预处理
            repayPlan = planService.getRepayPlanByBatchNo(repayPlan.getBatchNo());
            if (!Constants.PLAN_STATUS_IN_EXECUTE.equals(repayPlan.getStatus())) {
                return;
            }

            CommonResult result = repayService.executeRepay(repayParam);

            log.info("计划明细编号" + planNo + "执行还款返回：" + JsonUtil.objectToJson(result));

            if (result.getStatus() == 200) {

                TransferOrder resTransferOrder = (TransferOrder) result.getData();

                String resOrderNo = resTransferOrder.getSourceOrderNo();
                String resStatus = resTransferOrder.getStatus();
                String resCode = resTransferOrder.getSubResultCode();
                String resMsg = resTransferOrder.getSubResultMsg();
                String resTransferOrderNo = resTransferOrder.getSubOrderNo();

                int count;
                boolean isLast = false;

                if (Constants.TRANSFER_STATUS_SUCCESS.equals(resStatus)) {
                    //成功

                    //1、回写统一出账表相关信息
                    count = withdrawOrderService.writeBackWithdrawInfo(resOrderNo, Constants.WITHDRAW_STATUS_SUCCESS, null, resTransferOrderNo, resCode, resMsg);
                    log.info("回写统一出账表订单号：" + resOrderNo + "状态为成功影响的行数为" + count);

                    if (count > 0) {
                        //2、修改明细状态
                        count = planDetailService.updateDetailStatus(planNo, Constants.PLAN_DETAIL_STATUS_SUCCESS, null, resMsg);
                        log.info("更改代付计划明细编号：" + planNo + "状态为成功影响的行数为" + count);
                        if (count > 0) {
                            //判断是否是最后一笔，如果是最后一笔，就标识这个还款计划已完成
                            isLast = planDetailService.isLastIndexDetail(batchNo, planIndex);
                            if (isLast) {
                                log.info("明细编号：" + planNo + "是最后一笔还款计划，且代付成功，故标识整个还款完成");
                                count = planService.updateRepayPlanStatus(batchNo, Constants.PLAN_STATUS_SUCCESS, Constants.PLAN_STATUS_IN_EXECUTE, "还款完成");
                                log.info("更改计划批次号：" + batchNo + "为还款完成状态影响的行数" + count);

                            } else if (Constants.PLAN_EXPECT_STATUS_STOP.equals(repayPlan.getExpectStatus())) {
                                //如果不是最后一笔，判断此计划是否已预处理，如果是，则直接改为终止状态
                                log.info("计划批次号：" + batchNo + "已预处理，故直接改其批次状态为终止状态");
                                count = planService.updateRepayPlanStatus(batchNo, Constants.PLAN_STATUS_STOP, Constants.PLAN_STATUS_IN_EXECUTE, "用户终止");
                                log.info("更改计划批次号：" + batchNo + "为终止状态影响的行数" + count);
                            }
                        }
                    }

                } else if (Constants.TRANSFER_STATUS_SUBMIT_FAIL.equals(resStatus) || Constants.TRANSFER_STATUS_FAIL.equals(resStatus)) {
                    //失败

                    //1、回写统一出账表相关信息
                    count = withdrawOrderService.writeBackWithdrawInfo(resOrderNo, Constants.WITHDRAW_STATUS_FAIL, null, resTransferOrderNo, resCode, resMsg);
                    log.info("回写统一出账表订单号：" + resOrderNo + "状态为失败影响的行数为" + count);
                    if (count > 0) {
                        //2、修改明细状态
                        count = planDetailService.updateDetailStatus(planNo, Constants.PLAN_DETAIL_STATUS_FAIL, null, resMsg);
                        log.info("更改代付计划明细编号：" + planNo + "状态为失败影响的行数为" + count);
                        if (count > 0) {
                            //判断此明细执行次数是否达到最大值，如果已到，则此还款计划改为挂起状态
                            planDetail = planDetailService.getDetailByPlanNo(planNo);
                            int planCount = planDetail.getPlanCount();

                            if (Constants.PLAN_MAX_EXECUTE_COUNT <= planCount) {

                                log.info("计划明细编号：" + planNo + "执行次数已经达到" + planCount + "次，且操作次数已到" + Constants.PLAN_MAX_EXECUTE_COUNT + "" +
                                        "次，故将其对应批次号：" + batchNo + "状态改为挂起");
                                count = planService.updateRepayPlanStatus(batchNo, Constants.PLAN_STATUS_HANG, Constants.PLAN_STATUS_IN_EXECUTE, "计划挂起");
                                log.info("计划批次号：" + batchNo + "为挂起状态影响的行数" + count);
                            }
                        }
                    }
                } else {
                    //未知

                    //1、回写统一出账表相关信息
                    count = withdrawOrderService.writeBackWithdrawInfo(resOrderNo, Constants.WITHDRAW_STATUS_UNKNOW, null, resTransferOrderNo, resCode, resMsg);
                    log.info("回写统一出账表订单号：" + resOrderNo + "状态为未知影响的行数为" + count);
                    if (count > 0) {
                        //2、修改明细状态
                        count = planDetailService.updateDetailStatus(planNo, Constants.PLAN_DETAIL_STATUS_IN_EXECUTE, null, resMsg);
                        log.info("更改代付计划明细编号：" + planNo + "状态为执行中影响的行数为" + count);
                    }
                }
            } else {
                log.error("计划编号" + planNo + "代付失败：" + result.getMsg());
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("计划编号" + planNo + "执行代付异常：" + e.getMessage());
        }
    }


    /**
     * 发起消费
     *
     * @param repayPlan  还款计划
     * @param planDetail 还款明细
     */
    public void implQuickPay(RepayPlan repayPlan, RepayPlanDetail planDetail, QuickPayService quickPayService) {

        String batchNo = repayPlan.getBatchNo();
        String cardNo = planDetail.getCardNo();
        String amount = planDetail.getPlanAmount().toString();
        String planNo = planDetail.getPlanNo();
        String merchantNo = planDetail.getMerchantNo();
        int planIndex = planDetail.getPlanIndex();

        int count;
        boolean isLast = false;

        Map<String, String> repayParam = new HashMap<>();

        repayParam.put("cardNo", cardNo);
        repayParam.put("payAmount", amount);
        repayParam.put("merchantNo", merchantNo);
        repayParam.put("planNo", planNo);

        try {
            //跟新还款计划，防止预处理
            repayPlan = planService.getRepayPlanByBatchNo(repayPlan.getBatchNo());
            if (!Constants.PLAN_STATUS_IN_EXECUTE.equals(repayPlan.getStatus())) {
                return;
            }
            CommonResult result = quickPayService.executeQuickPay(repayParam);
            log.info("计划明细编号" + planNo + "快捷消费返回：" + JsonUtil.objectToJson(result));

            if (result.getStatus() == 200) {

                Map<String, String> payResData = (Map<String, String>) result.getData();

                String resCode = payResData.get("resCode");
                String resMsg = payResData.get("resMsg");
                String resStatus = payResData.get("resStatus");

                switch (resStatus) {
                    case Constants.ORDER_TRANS_STATUS_SUCCESS: {
                        count = planDetailService.updateDetailStatus(planNo, Constants.PLAN_DETAIL_STATUS_SUCCESS, null, resMsg);
                        log.info("更改消费计划明细编号：" + planNo + "状态为成功影响的行数为" + count);
                        if (count > 0) {
                            //判断是否是最后一笔，如果是最后一笔，就标识这个还款计划已完成
                            isLast = planDetailService.isLastIndexDetail(batchNo, planIndex);
                            if (isLast) {
                                log.info("计划明细编号：" + planNo + "是最后一笔还款计划，且消费成功，故标识整个还款完成");
                                count = planService.updateRepayPlanStatus(batchNo, Constants.PLAN_STATUS_SUCCESS, Constants.PLAN_STATUS_IN_EXECUTE, "还款完成");
                                log.info("更改计划批次号：" + batchNo + "为还款完成状态影响的行数" + count);
                            }
                        }
                        break;
                    }
                    case Constants.ORDER_TRANS_STATUS_FAIL: {
                        count = planDetailService.updateDetailStatus(planNo, Constants.PLAN_DETAIL_STATUS_FAIL, null, resMsg);
                        log.info("更改代付计划明细编号：" + planNo + "状态为失败影响的行数为" + count);

                        //判断此明细执行次数是否达到最大值，如果已到，则将此还款计划改为失败状态
                        planDetail = planDetailService.getDetailByPlanNo(planNo);
                        int planCount = planDetail.getPlanCount();

                        if (Constants.PLAN_MAX_EXECUTE_COUNT >= planCount) {
                            log.info("明细编号：" + planNo + "执行次数已经达到" + planCount + "次，且操作次数已到" + Constants.PLAN_MAX_EXECUTE_COUNT + "" +
                                    "次，故将其对应批次号：" + batchNo + "状态改为失败");
                            count = planService.updateRepayPlanStatus(batchNo, Constants.PLAN_STATUS_FAIL, Constants.PLAN_STATUS_IN_EXECUTE, "还款失败");
                            log.info("更改还款批次号：" + batchNo + "为还款失败状态影响的行数" + count);

                        } else if (Constants.PLAN_EXPECT_STATUS_STOP.equals(repayPlan.getExpectStatus())) {
                            //如果没有达到3次，判断此计划是否已预处理，如果是，则直接改为终止状态
                            log.info("计划批次号：" + batchNo + "已预处理，故直接改其批次状态为终止状态");
                            count = planService.updateRepayPlanStatus(batchNo, Constants.PLAN_STATUS_STOP, Constants.PLAN_STATUS_IN_EXECUTE, "用户终止");
                            log.info("更改计划批次号：" + batchNo + "为终止状态影响的行数" + count);
                        }
                        break;
                    }
                    case Constants.ORDER_TRANS_STATUS_UNKNOW: {
                        count = planDetailService.updateDetailStatus(planNo, Constants.PLAN_DETAIL_STATUS_IN_EXECUTE, null, resMsg);
                        log.info("更改代付计划明细编号：" + planNo + "状态为执行中影响的行数为" + count);
                        break;
                    }
                }
            } else {
                log.error("计划编号" + planNo + "快捷消费失败：" + result.getMsg());
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("计划编号" + planNo + "快捷消费异常：" + e.getMessage());
        }
    }

}
